﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace Nirvana
{
    [Serializable]
    public sealed class CheckModel
    {

        [SerializeField]
        [EnumLabel]
        [Tooltip("The readable check mode.")]
        private ReadableMode readableMode;

        [Tooltip("The scale factor for the model, 0 means no check.")]
        [SerializeField]
        private float scaleFactor;

        [Tooltip("The import material check mode.")]
        [EnumLabel]
        [SerializeField]
        private ImportMaterialMode importMaterialMode;

        [Tooltip("The weld Vertices check mode.")]
        [SerializeField]
        [EnumLabel]
        private WeldVerticesMode weldVerticesMode;
        

        [Tooltip("The optimize mesh check mode.")]
        [SerializeField]
        [EnumLabel]
        private OptimizeMeshMode optimizeMeshMode;

        [SerializeField]
        [EnumLabel]
        [Tooltip("The mesh compress check mode.")]
        private MeshCompressMode meshCompressMode;

        [Tooltip("The normal import check mode.")]
        [EnumLabel]
        [SerializeField]
        private NormalImportMode normalImportMode;

        [EnumLabel]
        [Tooltip("The tangent import check mode.")]
        [SerializeField]
        private TangentImportMode tangentImportMode;

        [EnumLabel]
        [Tooltip("The collider generate check mode.")]
        [SerializeField]
        private ColliderMode colliderMode;

        [EnumLabel]
        [Tooltip("The secondary UV generate check mode.")]
        [SerializeField]
        private SecondaryUVMode secondaryUVMode;

        [Tooltip("The animation mode.")]
        [EnumLabel]
        [SerializeField]
        private AnimationMode animationMode;

        [SerializeField]
        [EnumLabel]
        [Tooltip("The animation compress mode.")]
        private AnimationCompressMode animationCompressMode;

        [SerializeField]
        [EnumLabel]
        [Tooltip("The uv range mode.")]
        private UVRangeMode uvRangeMode;

        [Tooltip("The uv range tolerance.")]
        [SerializeField]
        private float uvTolerance;

        [EnumMask]
        [Tooltip("The must have vertex channel.")]
        [SerializeField]
        private VertexChannelMode mustHaveVertexChannel;

        [EnumMask]
        [SerializeField]
        [Tooltip("Can not have vertex channel.")]
        private VertexChannelMode canNotHaveVertexChannel;

        [SerializeField]
        [Tooltip("The max vertex count, 0 means no check.")]
        private int maxVertexCount;

        [Tooltip("The max triangle count, 0 means no check.")]
        [SerializeField]
        private int maxTriangleCount;

        [Tooltip("The max number of vertex trangle radio, if too large mean too much split vertexs.")]
        [SerializeField]
        private float maxVertexTriangleRadio = -1f;

        private enum ReadableMode
        {

            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Readable")]
            ForceReadable,
            [EnumLabel("Force Not Readable")]
            ForceNotReadable
        }

        private enum ImportMaterialMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Import")]
            ForceImport,
            [EnumLabel("Force Not Import")]
            ForceNotImport
        }

        private enum OptimizeMeshMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Optimize")]
            ForceOptimize,
            [EnumLabel("Force Not Optimize")]
            ForceNotOptimize
        }

        private enum MeshCompressMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Off")]
            ForceOff,
            [EnumLabel("At Least Low")]
            AtLeastLow,
            [EnumLabel("At Least Medium")]
            AtLeastMedium,
            [EnumLabel("At Least High")]
            AtLeastHigh
        }

        private enum WeldVerticesMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Weld")]
            ForceWeld,
            [EnumLabel("Force Not Weld")]
            ForceNotWeld
        }

        private enum NormalImportMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Import")]
            ForceImport,
            [EnumLabel("Force Calculate")]
            ForceCalculate,
            [EnumLabel("Force None")]
            ForceNode
        }
        private enum TangentImportMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Import")]
            ForceImport,
            [EnumLabel("Force Calculate")]
            ForceCalculate,
            [EnumLabel("Force None")]
            ForceNode
        }

        private enum ColliderMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Generate")]
            ForceGenerate,
            [EnumLabel("Force Not Generate")]
            ForceNotGenerate
        }
        private enum SecondaryUVMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Generate")]
            ForceGenerate,
            [EnumLabel("Force Not Generate")]
            ForceNotGenerate
        }
        private enum AnimationMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Legacy")]
            ForceLegacy,
            [EnumLabel("Force Generic")]
            ForceGenerate,
            [EnumLabel("Force Human")]
            ForceHuman,
            [EnumLabel("Force Not Legacy")]
            ForceNotLegacy
        }

        private enum AnimationCompressMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Force Off")]
            ForceOff,
            [EnumLabel("Force On")]
            ForceOn,
            [EnumLabel("Force Keyframe Reduction")]
            ForceKeyframeReduction,
            [EnumLabel("Force Keyframe Reduction And Compression")]
            ForceKeyframeReductionAndCompression,
            [EnumLabel("Force Optimize")]
            ForceOptimize
        }
        private enum UVRangeMode
        {
            [EnumLabel("Not Check")]
            NotCheck,
            [EnumLabel("Inside [0,1]")]
            Inside
        }

        private enum VertexChannelMode
        {
            [EnumLabel("Not Check")]
            NotCheck=0,
            [EnumLabel("Position")]
            Position = 1,
            [EnumLabel("Normal")]
            Normal,
            [EnumLabel("Tangent")]
            Tangent = 4,
            [EnumLabel("UV1")]
            UV1 = 8,
            [EnumLabel("UV2")]
            UV2 = 16,
            [EnumLabel("UV3")]
            UV3 = 32,
            [EnumLabel("UV4")]
            UV4 = 64,
            [EnumLabel("Color")]
            Color = 128
        }

        public void FilterModel(AssetCheckReport report,ModelImporter import)
        {
            if(this.readableMode == ReadableMode.ForceReadable)
            {
                if (!import.isReadable)
                {
                    report.ShowWarnInfo("The model is not readable, but it force readable.");
                }
            }
            else if(this.readableMode == ReadableMode.ForceNotReadable && import.isReadable)
            {
                report.ShowWarnInfo("The model is readable, but it force not readable.");
            }
            
            if(this.scaleFactor >0 && import.globalScale != this.scaleFactor)
            {
                report.ShowWarnInfo("The model scale factor is: <b><color=orange>{0}</color></b>, different from required scale: <b><color=orange>{1}</color></b>.",import.globalScale,this.scaleFactor);
            }

            if(this.importMaterialMode == ImportMaterialMode.ForceImport)
            {
                if (import.materialImportMode == ModelImporterMaterialImportMode.None)
                {
                    report.ShowWarnInfo("The model is not import material, but it require import.");
                }
            }
            else if(this.importMaterialMode == ImportMaterialMode.ForceNotImport)
            {
                if (import.materialImportMode != ModelImporterMaterialImportMode.None)
                {
                    report.ShowWarnInfo("The model is import material, but it require not import.");
                }
            }


            if (this.weldVerticesMode == WeldVerticesMode.ForceWeld)
            {
                if (!import.weldVertices)
                {
                    report.ShowWarnInfo("The model is not weld Vertices, but it require weldVertices.");
                }
            }
            else if (this.weldVerticesMode == WeldVerticesMode.ForceNotWeld)
            {
                if (import.weldVertices)
                {
                    report.ShowWarnInfo("The model is weld Vertices, but it require not weldVertices.");
                }
            }

            if (this.optimizeMeshMode == OptimizeMeshMode.ForceOptimize)
            {
                if (!import.optimizeMeshPolygons && !import.optimizeMeshVertices)
                {
                    report.ShowWarnInfo("The model is not optimize mesh, but it require optimize.");
                }
            }
            else if (this.optimizeMeshMode == OptimizeMeshMode.ForceNotOptimize)
            {
                if (import.optimizeMeshPolygons || import.optimizeMeshVertices)
                {
                    report.ShowWarnInfo("The model is optimize mesh, but it require not optimize.");
                }
            }


            
            if (this.meshCompressMode == MeshCompressMode.ForceOff)
            {
                if(import.meshCompression != ModelImporterMeshCompression.Off)
                {
                    report.ShowWarnInfo("The model compression level is <b><color=orange>{0}</color></b>, but it require turn off the compression.",import.meshCompression);
                }
            }
            else if (this.meshCompressMode == MeshCompressMode.AtLeastLow)
            {
                if (import.meshCompression == ModelImporterMeshCompression.Off)
                {
                    report.ShowWarnInfo("The model compression level is <b><color=orange>{0}</color></b>, but it require at least low compression.", import.meshCompression);
                }
            }
            else if (this.meshCompressMode == MeshCompressMode.AtLeastMedium)
            {
                if (import.meshCompression == ModelImporterMeshCompression.Off|| import.meshCompression == ModelImporterMeshCompression.Low)
                {
                    report.ShowWarnInfo("The model compression level is <b><color=orange>{0}</color></b>, but it require at least medium compression.", import.meshCompression);
                }
            }
            else if (this.meshCompressMode == MeshCompressMode.AtLeastHigh)
            {
                if (import.meshCompression == ModelImporterMeshCompression.Off || import.meshCompression == ModelImporterMeshCompression.Low || import.meshCompression == ModelImporterMeshCompression.Medium)
                {
                    report.ShowWarnInfo("The model compression level is <b><color=orange>{0}</color></b>, but it require at least high compression.", import.meshCompression);
                }
            }

            if (this.normalImportMode == NormalImportMode.ForceCalculate)
            {
                if (import.importNormals != ModelImporterNormals.Calculate)
                {
                    report.ShowWarnInfo("The model import normals is <b><color=orange>{0}</color></b>, but it require calculate by unity.",  import.importNormals );
                }
            }
            else if (this.normalImportMode == NormalImportMode.ForceImport)
            {
                if (import.importNormals != ModelImporterNormals.Import)
                {
                    report.ShowWarnInfo("The model import normals is <b><color=orange>{0}</color></b>, but it require import from file.", import.importNormals);
                }
            }
            else if (this.normalImportMode == NormalImportMode.ForceNode && import.importNormals != ModelImporterNormals.None)
            {
                report.ShowWarnInfo("The model import normals is <b><color=orange>{0}</color></b>, but it require do not import normal.", import.importNormals);
            }

            if (this.tangentImportMode == TangentImportMode.ForceCalculate)
            {
                if (import.importTangents != ModelImporterTangents.CalculateMikk)
                {
                    report.ShowWarnInfo("The model import tangents is <b><color=orange>{0}</color></b>, but it require calculate by unity.", import.importTangents);
                }
            }
            else if (this.tangentImportMode == TangentImportMode.ForceImport)
            {
                if (import.importTangents == ModelImporterTangents.None)
                {
                    report.ShowWarnInfo("The model import tangents is <b><color=orange>{0}</color></b>, but it require import from file.", import.importTangents);
                }
            }
            else if (this.tangentImportMode == TangentImportMode.ForceNode && import.importTangents != ModelImporterTangents.None)
            {
                report.ShowWarnInfo("The model import tangents is <b><color=orange>{0}</color></b>, but it require do not import tangents.", import.importTangents);
            }

            if (this.colliderMode == ColliderMode.ForceGenerate)
            {
                if (!import.addCollider)
                {
                    report.ShowWarnInfo("The model is not generate collider, but it require generate.");
                }
            }
            else if (this.colliderMode == ColliderMode.ForceNotGenerate && import.addCollider)
            {
                report.ShowWarnInfo("The model is generate collider, but it require not generate.");
            }

            if (this.secondaryUVMode == SecondaryUVMode.ForceGenerate)
            {
                if (!import.generateSecondaryUV)
                {
                    report.ShowWarnInfo("The model is not generate secondary UV, but it require generate.");
                }
            }
            else if (this.secondaryUVMode == SecondaryUVMode.ForceNotGenerate && import.generateSecondaryUV)
            {
                report.ShowWarnInfo("The model is generate secondary UV, but it require not generate.");
            }

            //animation
            if(import.importAnimation && import.importedTakeInfos != null && import.importedTakeInfos.Length > 0)
            {
                if(this.animationMode == AnimationMode.ForceLegacy)
                {
                    if(import.animationType != ModelImporterAnimationType.Legacy)
                    {
                        report.ShowWarnInfo("The model animation type is <b><color=orange>{0}</color></b>, but it require use legacy animation type.", import.animationType);
                    }
                }
                else if (this.animationMode == AnimationMode.ForceGenerate)
                {
                    if (import.animationType != ModelImporterAnimationType.Generic)
                    {
                        report.ShowWarnInfo("The model animation type is <b><color=orange>{0}</color></b>, but it require use generic animation type.", import.animationType);
                    }
                }
                else if (this.animationMode == AnimationMode.ForceHuman)
                {
                    if (import.animationType != ModelImporterAnimationType.Human)
                    {
                        report.ShowWarnInfo("The model animation type is <b><color=orange>{0}</color></b>, but it require use humanoid animation type.", import.animationType);
                    }
                }
                else if (this.animationMode == AnimationMode.ForceNotLegacy && import.animationType == ModelImporterAnimationType.Legacy)
                {
                    report.ShowWarnInfo("The model animation type is legacy, but it require not use legacy mode.");
                }


                if (this.animationCompressMode == AnimationCompressMode.ForceOff)
                {
                    if (import.animationCompression != ModelImporterAnimationCompression.Off)
                    {
                        report.ShowWarnInfo("The model animation compress type is <b><color=orange>{0}</color></b>, but it require turn off the compress.", import.animationCompression);
                    }
                }
                else if (this.animationCompressMode == AnimationCompressMode.ForceOn)
                {
                    if (import.animationCompression == ModelImporterAnimationCompression.Off)
                    {
                        report.ShowWarnInfo("The model animation compress type is <b><color=orange>{0}</color></b>, but it require turn on the compress.", import.animationCompression);
                    }
                }
                else if (this.animationCompressMode == AnimationCompressMode.ForceKeyframeReduction)
                {
                    if (import.animationCompression != ModelImporterAnimationCompression.KeyframeReduction)
                    {
                        report.ShowWarnInfo("The model animation compress type is <b><color=orange>{0}</color></b>, but it require to use keyframe reduction.", import.animationCompression);
                    }
                }
                else if (this.animationCompressMode == AnimationCompressMode.ForceKeyframeReductionAndCompression)
                {
                    if (import.animationCompression != ModelImporterAnimationCompression.KeyframeReductionAndCompression)
                    {
                        report.ShowWarnInfo("The model animation compress type is <b><color=orange>{0}</color></b>, but it require to use keyframe reduction and compression.", import.animationCompression);
                    }
                }
                else if (this.animationCompressMode == AnimationCompressMode.ForceOptimize && import.animationCompression != ModelImporterAnimationCompression.Optimal)
                {
                    report.ShowWarnInfo("The model animation compress type is <b><color=orange>{0}</color></b>, but it require to use optimal.", import.animationCompression);
                }

            }
        }

        public void FilterMesh(AssetCheckReport report, Mesh mesh)
        {
            if(mesh.uv != null && this.uvRangeMode == UVRangeMode.Inside)
            {
                foreach(var p in mesh.uv)
                {
                    if(p.x<0-this.uvTolerance - Mathf.Epsilon || p.x >1+this.uvTolerance + Mathf.Epsilon||
                        p.y <0-this.uvTolerance -Mathf.Epsilon || p.y > 1 + this.uvTolerance + Mathf.Epsilon)
                    {
                        report.ShowWarnInfo("The mode {0} uv is out of range: <b><color=orange>({1},{2})</color></b>",
                            mesh.name,
                            p.x,
                            p.y);
                        break;
                    }
                }
            }

            if(this.mustHaveVertexChannel != 0)
            {
                if((this.mustHaveVertexChannel & VertexChannelMode.Position )!= 0 &&(mesh.vertices == null || mesh.vertices.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>position</color></b> vertex channel.", mesh.name);
                }

                if ((this.mustHaveVertexChannel & VertexChannelMode.Normal) != 0 && (mesh.normals == null || mesh.normals.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>normal</color></b> vertex channel.", mesh.name);
                }

                if ((this.mustHaveVertexChannel & VertexChannelMode.Tangent) != 0 && (mesh.tangents == null || mesh.tangents.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>tangents</color></b> vertex channel.", mesh.name);
                }

                if ((this.mustHaveVertexChannel & VertexChannelMode.UV1) != 0 && (mesh.uv == null || mesh.uv.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>uv</color></b> vertex channel.", mesh.name);
                }
                if ((this.mustHaveVertexChannel & VertexChannelMode.UV2) != 0 && (mesh.uv2 == null || mesh.uv2.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>uv2</color></b> vertex channel.", mesh.name);
                }
                if ((this.mustHaveVertexChannel & VertexChannelMode.UV3) != 0 && (mesh.uv3 == null || mesh.uv3.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>uv3</color></b> vertex channel.", mesh.name);
                }
                if ((this.mustHaveVertexChannel & VertexChannelMode.UV4) != 0 && (mesh.uv4 == null || mesh.uv4.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>uv4</color></b> vertex channel.", mesh.name);
                }

                if ((this.mustHaveVertexChannel & VertexChannelMode.Color) != 0 && (mesh.colors == null || mesh.colors.Length == 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> dose not have <b><color=orange>colors</color></b> vertex channel.", mesh.name);
                }

            }

            if (this.canNotHaveVertexChannel != 0)
            {
                if ((this.canNotHaveVertexChannel & VertexChannelMode.Position) != 0 && (mesh.vertices != null && mesh.vertices.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>position</color></b> vertex channel.", mesh.name);
                }

                if ((this.canNotHaveVertexChannel & VertexChannelMode.Normal) != 0 && (mesh.normals != null && mesh.normals.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b>can not have <b><color=orange>normal</color></b> vertex channel.", mesh.name);
                }

                if ((this.canNotHaveVertexChannel & VertexChannelMode.Tangent) != 0 && (mesh.tangents != null && mesh.tangents.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>tangents</color></b> vertex channel.", mesh.name);
                }

                if ((this.canNotHaveVertexChannel & VertexChannelMode.UV1) != 0 && (mesh.uv != null && mesh.uv.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>uv</color></b> vertex channel.", mesh.name);
                }
                if ((this.canNotHaveVertexChannel & VertexChannelMode.UV2) != 0 && (mesh.uv2 != null && mesh.uv2.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have<b><color=orange>uv2</color></b> vertex channel.", mesh.name);
                }
                if ((this.canNotHaveVertexChannel & VertexChannelMode.UV3) != 0 && (mesh.uv3 != null && mesh.uv3.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>uv3</color></b> vertex channel.", mesh.name);
                }
                if ((this.canNotHaveVertexChannel & VertexChannelMode.UV4) != 0 && (mesh.uv4 != null && mesh.uv4.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>uv4</color></b> vertex channel.", mesh.name);
                }

                if ((this.canNotHaveVertexChannel & VertexChannelMode.Color) != 0 && (mesh.colors != null && mesh.colors.Length > 0))
                {
                    report.ShowWarnInfo("The mode <b><color=orange>{0}</color></b> can not have <b><color=orange>colors</color></b> vertex channel.", mesh.name);
                }

            }

            if (this.maxVertexCount > 0 && mesh.vertexCount > this.maxVertexCount)
            {
                report.ShowWarnInfo("The mesh <b><color=orange>{0}</color></b> vertex count is <b><color=orange>{1}</color></b>, more than the limit <b><color=orange>{2}</color></b>",
                    mesh.name,
                    mesh.vertexCount,
                    this.maxVertexCount
                );
            }

            if (this.maxTriangleCount > 0)
            {
                int num = mesh.triangles.Length / 3;
                if (num > this.maxTriangleCount)
                {
                    report.ShowWarnInfo("The mesh <b><color=orange>{0}</color></b> triangle count is <b><color=orange>{1}</color></b>, more than the limit <b><color=orange>{2}</color></b>", 
                        mesh.name,
                        num,
                        this.maxTriangleCount
                    );
                }
            }

            if (this.maxVertexTriangleRadio > 0f)
            {
                int num2 = mesh.triangles.Length / 3;
                int vertexCount = mesh.vertexCount;
                float num3 = (float)vertexCount / (float)num2;
                if (num3 > this.maxVertexTriangleRadio)
                {
                    report.ShowWarnInfo("The mesh <b><color=orange>{0}</color></b> has <b><color=orange>{1}</color></b> trangles, but <b><color=orange>{2}</color></b> vertexs, vertex is too much",
                        mesh.name,
                        num2,
                        vertexCount
                    );
                }
            }


        }

    }
}

